#include "stdafx.h"
#include "thread.h"
#include "process.h"

namespace thinvm
{
	tthread_t::tthread_t( tprocess_t * proc ) 
		: IA32_CPU(proc)
		, _process(proc)
		, _tid(0)
		, _teb_base(0)
		, _stack_low(0)
		, _stack_high(0)
	{

	}

	long tthread_t::create( uint32_t tid, bx_uint32_t entry, bx_uint32_t teb_base, bx_uint32_t stack_low, bx_uint32_t stack_size )
	{
		if( !_process ) return E_UNEXPECTED;

		size_t stack_pages = (stack_size>>PAGE4K_SHIFT);
		if( !stack_pages ) return E_INVALIDARG;

		void * teb_page = _process->map_alloc_page( teb_base, VMPA_READ|VMPA_WRITE );
		if( !teb_page ) return E_OUTOFMEMORY;

		init_teb_page( teb_page );

		size_t c = _process->map_alloc_pages( stack_low, stack_pages, VMPA_READ|VMPA_WRITE|VMPA_MOA, 0 );
		if( c != stack_pages )
		{
			_process->mm_unmap( teb_base );
			return E_OUTOFMEMORY;
		}

		_tid = tid;
		_teb_base = teb_base;
		_stack_low = stack_low;
		_stack_high = stack_low + stack_size;

		load_segment_cache(IA32_SEG_REG_CS,0,MAX_PAGES_IN_2G,3);
		load_segment_cache(IA32_SEG_REG_DS,0,MAX_PAGES_IN_2G,3);
		load_segment_cache(IA32_SEG_REG_ES,0,MAX_PAGES_IN_2G,3);
		load_segment_cache(IA32_SEG_REG_SS,0,MAX_PAGES_IN_2G,3);
		load_segment_cache(IA32_SEG_REG_GS,0,MAX_PAGES_IN_2G,3);
		load_segment_cache(IA32_SEG_REG_FS, teb_base, PAGE4K_SIZE,3);
		set_reg_value( IA32_32BIT_REG_ESP, _stack_high );
		set_xip( entry );

		return S_OK;
	}
	bool tthread_t::push( bx_uint32_t val )
	{
		bx_uint32_t esp = ESP;
		if( esp < sizeof(bx_uint32_t) ) return false;
		size_t eff = 0;
		vmo()->access_address_n( 0, esp-sizeof(bx_uint32_t), sizeof(bx_uint32_t), 0, IA32_WRITE, &val, &eff );
		if( eff != sizeof(bx_uint32_t) ) return false;
		ESP -= sizeof(bx_uint32_t);
		return true;
	}
	bool tthread_t::ret_n( Bit16u n )
	{
		bx_uint32_t esp = ESP;
		size_t eff = 0;
		bx_uint32_t ret_addr = 0;
		vmo()->access_address_n( 0, esp, sizeof(bx_uint32_t), 0, IA32_READ, &ret_addr, &eff );
		if( eff != sizeof(bx_uint32_t) ) return false;
		EIP = ret_addr;
		ESP += (n + sizeof(bx_uint32_t));
		return true;
	}
	bool tthread_t::ret_eax_n( Bit16u n, bx_uint32_t ret_val )
	{
		bx_uint32_t esp = ESP;
		size_t eff = 0;
		bx_uint32_t ret_addr = 0;
		vmo()->access_address_n( 0, esp, sizeof(bx_uint32_t), 0, IA32_READ, &ret_addr, &eff );
		if( eff != sizeof(bx_uint32_t) ) return false;
		EIP = ret_addr;
		ESP += (n + sizeof(bx_uint32_t));
		EAX = ret_val;
		return true;
	}
	size_t tthread_t::execute( size_t steps, size_t check_bits )
	{
		return cpu_loop( steps, check_bits );
	}
};